package ldh.im.fxgui.controller;

import com.google.common.eventbus.Subscribe;
import com.jfoenix.controls.JFXDialog;
import javafx.beans.InvalidationListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.Window;
import ldh.im.ImConfig;
import ldh.im.fxbase.dao.*;
import ldh.im.fxbase.data.dto.SearchResult;
import ldh.im.fxbase.data.event.EventBusFactory;
import ldh.im.fxbase.data.event.NewMessageSession;
import ldh.im.fxbase.data.event.SessionChange;
import ldh.im.fxbase.data.pojo.*;
import ldh.im.fxbase.util.*;
import ldh.im.fxgui.component.SearchResultPane;
import ldh.im.fxgui.component.cell.SessionView;
import ldh.im.fxgui.controller.sub.AddUserToGroupController;
import ldh.im.fxbase.util.ImCacheFactory;
import ldh.im.fxgui.service.ContentSearchService;
import ldh.im.fxgui.util.UiUtil;
import ldh.im.util.ConfigUtil;
import ldh.rpc.cache.CacheService;
import org.controlsfx.control.textfield.CustomTextField;
import org.fxmisc.flowless.Cell;
import org.fxmisc.flowless.VirtualFlow;
import org.fxmisc.flowless.VirtualizedScrollPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URL;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 消息会话列表
 */
public class MessageSessionController extends BaseController implements Initializable {

    private static final Logger LOGGER = LoggerFactory.getLogger(MessageSessionController.class);

    @FXML private Pane sendMessagePane;              // 消息面板容器
    @FXML private Pane messageSessionPane;
    @FXML private CustomTextField searchTextField;

    private ObservableList<ImSession> messageSessionDatas = FXCollections.observableArrayList();
    private FilteredList<ImSession> filteredData = new FilteredList<>(messageSessionDatas, s -> true);
    private SortedList<ImSession> sortedList = new SortedList<ImSession>(filteredData, this::messageSessionComparator);

    private VirtualFlow<ImSession, ?> messageSessionList;
    private VirtualizedScrollPane sessionScrollPane;

    private SearchResultPane searchPane = new SearchResultPane(this);
    private volatile ImSession selectSession = null;
    private volatile MessageSendController selectMessageSendController = null;
    private volatile Node selectedNode = null;

    private Map<Long, SessionView> messageSessionNodeMap = new HashMap<>();

    public void loadMessageSessionData(Optional<ImSession> sessionOptional) {
        LOGGER.info("load message session data, {}", messageSessionDatas.size());
        CacheService cacheService = RpcUtil.getRpcBootstrap().getRpcConfig().getCacheService();
        String userId = (String) cacheService.getCache(ImConfig.LOGIN_USER_ID);
        if (userId == null) return;
        ThreadPoolUtil.submit(()->{
            ExceptionUtil.catchException(()->{
                int showPreDay = ConfigUtil.getInt("message.session.showDay", -14);
                LocalDateTime startDay = LocalDateTime.now().plusDays(showPreDay);

                ImSessionDao imSessionDao = BeanFactory.getInstance().getBean(ImSessionDao.class);
                List<ImSession> imSessions = imSessionDao.getByUserIdAndTimeAndSize(userId, startDay, 0, 1000);

                List<ImSession> sessions = new ArrayList<>();
                for(ImSession imSession : imSessions) {
                    try {
                        initImMessageSessionToUserOrGroupUsers(imSession);
                        sessions.add(imSession);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                if (sessions.size() > 0) {
                    FxThreadUtil.runLater(()->{
                        messageSessionDatas.addAll(sessions);
                        if (sessionOptional.isPresent()) {
                            int selectIdx = sortedList.indexOf(sessionOptional.get());
                            messageSessionList.show(selectIdx);
                            showSession(sessions.get(selectIdx), 300);
                        } else {
                            messageSessionList.show(0);
                        }
                    });
                }
                return null;
            }, "加载会话信息");
        });
    }

    public void loadLastedMessage(Optional<ImSession> sessionOption) {
        if (selectMessageSendController != null) {
            selectMessageSendController.loadLastedMessage();
            if (sessionOption.isPresent()) {
                switchSession(sessionOption.get());
            }
        }
    }

    public void updateSession(ImSession session) {
        List<ImSession> mItems = sortedList;
        int index = mItems.indexOf(session);
        if (index >= 0) {
            ImSession item = mItems.get(index);
            item.setLatelyMessage(session.getLatelyMessage());
        }
    }

    public void switchSession(ImSession imSession) {
        LOGGER.info("switch session, id: {}", imSession.getId());
        if (imSession == null) {
            LOGGER.error("imSession is null");
            return;
        }
        if (selectSession != null && selectSession == imSession) {
            LOGGER.debug("switch session is same");
            return;
        }

        Window window = messageSessionList.getScene().getWindow();
        if (window instanceof Stage) {
            ((Stage) window).toFront();
        }
        cleanSearchText();
        if (messageSessionDatas.contains(imSession)) {
            LOGGER.info("switch session contain, id: {}", imSession.getId());
            List<ImSession> mItems = messageSessionDatas;
            ImSession item = mItems.stream().filter(m->m.getSoftId().equals(imSession.getSoftId())).findFirst().get();
            messageSessionList.show(messageSessionDatas.indexOf(item));
            messageSessionList.getCell(messageSessionDatas.indexOf(item));
            SessionView sessionView = messageSessionNodeMap.get(item.getId());
            if (sessionView != null) {
                sessionView.fireEvent(new MouseEvent(MouseEvent.MOUSE_CLICKED, 1, 1, 1, 1, MouseButton.PRIMARY, 1,
                        true, true, true, true, true, true, true, true, true, true, null));
            }
        } else {
            LOGGER.info("switch session add, id: {}, size: {}", imSession.getId(), messageSessionDatas.size());
            messageSessionDatas.add(imSession);
            showSession(imSession, 300);
        }
    }

    /**
     * 更新某个会话
     * @param mItem
     */
    public void reloadMessageSession(ImSession mItem) {
        messageSessionDatas.remove(mItem);
        messageSessionDatas.add(mItem);
        messageSessionList.show(sortedList.indexOf(mItem));
    }

    /**
     * 移除某个会话
     * @param messageSession
     */
    public void removeMessageSession(ImSession messageSession) {
        removeMessageSessionPane(messageSession);
        if (selectSession == messageSession) {
            cleanSendMessagePane();

            selectSession = null;

            UiUtil.setSendMessageHandler(null);
        }
    }

    /**
     * 创建群
     * @param e
     */
    public void createGroupAct(ActionEvent e) {
        RegionUtil.FxmlBean<Pane, AddUserToGroupController> fxmlBean = RegionUtil.loadFxmlBean(this.getClass().getResource("/fxml/sub/AddUserToGroupPane.fxml"));
        JFXDialog dialog = DialogUtil.show(fxmlBean.getNode(), false);
        dialog.setPrefSize(700, 450);
        fxmlBean.getController().setDialog(dialog);
        fxmlBean.getController().setCurrentImSession(null);
    }

    /**
     * 用户信息发送变化，如：头像
     * @param imUser
     */
    @Subscribe
    public void userChange(ImUser imUser) {
        FxThreadUtil.runLater(()->{
            List<ImSession> imSessionList = messageSessionDatas.filtered(g->g.isP2p() && g.getToUserId().equals(imUser.getUserId()));
            for(ImSession imSession : imSessionList) {
                int idx = messageSessionDatas.indexOf(imSession);
                if (idx >= 0) {
                    messageSessionDatas.remove(idx);
                    imSession.setToImUser(imUser);
                    messageSessionDatas.add(idx, imSession);
                    messageSessionList.requestFocus();
                }
            }
        });
    }

    @Subscribe
    public void sessionChange(SessionChange sessionChange) {
        ThreadPoolUtil.submit(()->{
            try {
                List<ImSession> sessions = messageSessionDatas.filtered((ms)->ms.getId() == sessionChange.getSessionId());
                if (sessions.size() < 1) {
                    return;
                }

                ImSessionDao imSessionDao = BeanFactory.getInstance().getBean(ImSessionDao.class);
                ImGroupDao imGroupDao = BeanFactory.getInstance().getBean(ImGroupDao.class);

                ImSession session = imSessionDao.getSessionById(sessionChange.getSessionId());
                ImGroup group = imGroupDao.getByUserIdAndId(session.getCurrentUserId(), session.getGroupCode());
                session.setImGroup(group);
                initImMessageSessionToUserOrGroupUsers(session);
                FxThreadUtil.runLater(()->{
                    messageSessionDatas.replaceAll((s)->{
                        if (s.getId() == sessionChange.getSessionId()) {
                            return session;
                        }
                        return s;
                    });
                });
            } catch (Exception e) {
                LOGGER.error("加载会话失败", e);
            }
        });
    }

    /**
     * 增加新的会话
     * @param newMessageSession
     */
    @Subscribe
    public void addNewMessageSession(NewMessageSession newMessageSession) {
        if (selectSession != null && selectSession.getId().equals(newMessageSession.getMessageSessionId())) return;
        Optional<ImSession> imMessageSessionOption = messageSessionDatas.stream().filter(ms->ms.getId().equals(newMessageSession.getMessageSessionId())).findFirst();
        if (imMessageSessionOption.isPresent()) {
//            ImSession imSession = imMessageSessionOption.get();
//            if (selectSession != null && selectSession == imSession) return;
//            FxThreadUtil.runLater(()->{
//                cleanSearchText();
//                messageSessionDatas.replaceAll((session)->{
//                    if (session.getId() == imSession.getId()) {
//                        return imSession;
//                    }
//                    return session;
//                });
//            });
            return;
        }
        ThreadPoolUtil.submit(()->{
            try {
                ImSessionDao imSessionDao = BeanFactory.getInstance().getBean(ImSessionDao.class);
                ImSession imSession = imSessionDao.getSessionById(newMessageSession.getMessageSessionId());
                initImMessageSessionToUserOrGroupUsers(imSession);
                FxThreadUtil.runLater(()->{
                    messageSessionDatas.add(imSession);
                });
            } catch (Exception e) {
                LOGGER.error("加载会话数据失败", e);
            }
        });
    }

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        messageSessionList = VirtualFlow.createVertical(sortedList, m->{
            SessionView node = null; //messageSessionNodeMap.get(m.getId());
            if (node == null) {
                node = createMessageSessionPane(m);

                cacheMessageSessionPane(m, node);

                if (selectSession == m) {
                    node.getStyleClass().add("selected-session");
                }
            } else {
                node.setData(m);
            }

            SessionView nodePane = node;
            node.setData(m);
            node.setOnMouseClicked(e->{
                selectNode(m, nodePane);
            });
            return Cell.wrapNode(node);
        });

        sessionScrollPane = new VirtualizedScrollPane<>(messageSessionList);

        searchPane.setVisible(false);
        messageSessionPane.getChildren().addAll(sessionScrollPane, searchPane);

        searchTextField.textProperty().addListener((InvalidationListener)l->{
            String filter = searchTextField.getText().trim();
            if (filter.equals("")) {
                searchPane.setVisible(false);
                sessionScrollPane.setVisible(true);
            } else {
                searchPane.setVisible(true);
                sessionScrollPane.setVisible(false);

                loadSearchData(filter);
            }
        });

        sessionScrollPane.setOnKeyPressed(e->{
            if (e.getCode() == KeyCode.UP) {
                up();
            } else if (e.getCode() == KeyCode.DOWN) {
                down();
            }
        });
    }

    /**
     * 根据会话栏，加载消息栏
     * @param imSession
     */
    private void changeSendMessagePane(ImSession imSession) {
        if (imSession == null) return;
        if (selectSession != null && imSession.getId().equals(selectSession.getId())) {
            return;
        }
        LOGGER.info("changeSendMessagePane messageSessionId {}", imSession.getId());

        ThreadPoolUtil.submit(()->{
            try {
                if (selectMessageSendController != null) {
                    selectMessageSendController.unRegisterEvent();
                    selectMessageSendController = null;
                }
                FxThreadUtil.runLater(()->{
                    String key = "/fxml/MessageSendPane.fxml";
                    RegionUtil.FxmlBean<Node, MessageSendController> fxmlBean = SoftMapUtil.getWeakCache(key);
                    if (fxmlBean == null) {
                        fxmlBean = RegionUtil.loadFxmlBean(this.getClass().getResource(key));
                        SoftMapUtil.weakCache(key, fxmlBean);
                    }

                    fxmlBean.getController().setMessageSessionController(this);
                    fxmlBean.getController().setImSession(imSession);
                    selectMessageSendController = fxmlBean.getController();
                    cleanSendMessagePane();
                    VBox.setVgrow(fxmlBean.getNode(), Priority.ALWAYS);
                    sendMessagePane.getChildren().add(fxmlBean.getNode());

                    selectMessageSendController.requestFocus();
                });
            } catch (Exception e) {
                LOGGER.error("load MessageSendController error", e);
            }
        });
    }

    /**
     * 会话栏排序
     * @param messageSession
     * @param messageSession2
     * @return
     */
    private int messageSessionComparator(ImSession messageSession, ImSession messageSession2) {
        String userId = UiUtil.getLoginImUser().getUserId();
        boolean isTop = false;
        Date updateTime = messageSession.getUpdateTime();
        boolean isTop2 = false;
        Date updateTime2 = messageSession2.getUpdateTime();

        if (!messageSession.isP2p()) { //群聊
            Set<ImGroupUser> groupUsers1 = messageSession.getImGroup().getGroupUserSet();
            ImGroupUser groupUser1 = groupUsers1.stream().filter(gu->gu.getUserId().equals(userId)).findFirst().get();
            isTop = groupUser1.getTop();
        } else { // 端对端聊天
            isTop = messageSession.getFromUserId().compareTo(messageSession.getToUserId()) >= 0 ? messageSession.isTop() : messageSession.isTop2();
        }

        if (!messageSession2.isP2p()) { //群聊
            Set<ImGroupUser> groupUsers2 = messageSession2.getImGroup().getGroupUserSet();
            ImGroupUser groupUser2 = groupUsers2.stream().filter(gu->gu.getUserId().equals(userId)).findFirst().get();
            isTop2 = groupUser2.getTop();
        } else { // 端对端聊天
            isTop2 = messageSession2.getFromUserId().compareTo(messageSession2.getToUserId()) >= 0 ? messageSession2.isTop() : messageSession2.isTop2();
        }

        if (isTop && isTop2) {
            int ht = messageSession.getHotMessageTotal().compareTo(messageSession2.getHotMessageTotal());
            int ut = updateTime2.compareTo(updateTime);
            if (ht == 0) {
                return ut;
            }
            return ht;
        }
        if (isTop) return -1;
        if (isTop2) return 1;

        int ht = messageSession2.getHotMessageTotal().compareTo(messageSession.getHotMessageTotal());
        int ut = updateTime2.compareTo(updateTime);
        if (ht == 0) {
            return ut;
        }
        return ht;
    }

    /**
     * 清空会话栏查找信息
     */
    public void cleanSearchText() {
        this.searchTextField.setText("");
    }

    private SessionView createMessageSessionPane(ImSession session) {
//        String key = "ImSession" + session.getId();
////        SessionView sessionView = SoftMapUtil.getCache(key);
////        if (sessionView == null) {
////            sessionView = new SessionView(session, this);
////            SoftMapUtil.cache(key, sessionView);
////        }
////        return sessionView;
        return new SessionView(session, this);
    }

    /**
     * 会话栏支持上下键向上或向下移动
     */
    public void up() {
        if (selectSession == null) return;
        int idx = sortedList.indexOf(selectSession);
        int selectIndex = idx;
        if (idx > 0) {
            selectIndex--;
        }
        ImSession imSession = sortedList.get(selectIndex);
        showSession(imSession, 0);
    }

    /**
     * 会话栏支持上下键向上或向下移动
     */
    public void down() {
        if (selectSession == null) return;
        int idx = sortedList.indexOf(selectSession);
        int selectIndex = idx;
        if (idx < sortedList.size()-1) {
            selectIndex++;
        }
        ImSession selectSession = sortedList.get(selectIndex);
        showSession(selectSession, 0);
    }


    /**
     * 通过node选择某个会话栏，消息栏跟着会话栏变化
     * @param node
     */
    private void selectNode(ImSession session, Node node) {
        UiUtil.deleteCacheWrapImMessage(session.getId().toString());
        if (selectSession == session) return;
        if (selectedNode != null) {
            selectedNode.getStyleClass().remove("selected-session");
        }
        changeSendMessagePane(session);
        selectSession = session;
        selectedNode = node;
        if (!node.getStyleClass().contains("selected-session")) {
            node.getStyleClass().add("selected-session");
        }
    }

    // 缓存会话面板
    private void cacheMessageSessionPane(ImSession messageSession, SessionView sessionView) {
        messageSessionNodeMap.put(messageSession.getId(), sessionView);
        EventBusFactory.getInstance().register(sessionView);
    }

    // 清理缓存会话面板
    private void removeMessageSessionPane(ImSession messageSession) {
        messageSessionDatas.remove(messageSession);
        SessionView sessionView = messageSessionNodeMap.remove(messageSession.getId());
        sessionView.dispose();
        EventBusFactory.getInstance().unregister(sessionView);
    }

    private void initImMessageSessionToUserOrGroupUsers(ImSession imSession) throws SQLException {
        if (imSession.getToImUser() != null) return;
        ImUserDao imUserDao = BeanFactory.getInstance().getBean(ImUserDao.class);
        ImGroupUserDao imGroupUserDao = BeanFactory.getInstance().getBean(ImGroupUserDao.class);

        if (imSession.isP2p()) {
            ImUser imUser = ImCacheFactory.getInstance().getUser(imSession.getToUserId());
            if (imUser == null) {
                imUser = imUserDao.getUserById(imSession.getToUserId());
            }
            imSession.setToImUser(imUser);
            if(imUser == null) {
                throw new RuntimeException("没有此用户：" + imSession.getToUserId());
            }
            ImCacheFactory.getInstance().putUser(imUser.getUserId(), imUser);
        } else {
            ImGroup imGroup = imSession.getImGroup();
            Set<ImGroupUser> groupUsers = imGroup.getGroupUserSet();
            if (groupUsers == null) {
                groupUsers = imGroupUserDao.getGroupUsersByGroupId(imGroup.getId());
                imGroup.setGroupUserSet(groupUsers);
            }
            for(ImGroupUser gu : groupUsers) {
                ImUser imUser = ImCacheFactory.getInstance().getUser(gu.getUserId());
                if (imUser == null) {
                    imUser = imUserDao.getUserById(gu.getUserId());
                }
                gu.setUser(imUser);
                ImCacheFactory.getInstance().putUser(imUser.getUserId(), imUser);
            }

            ImCacheFactory.getInstance().putImGroup(imGroup.getId(), imGroup);
        }
    }

    private void cleanSendMessagePane() {
        int size = sendMessagePane.getChildren().size();
        if (size > 1) {
            sendMessagePane.getChildren().remove(1, size);
        }
    }

    private void loadSearchData(String search) {
        String userId = UiUtil.getLoginImUser().getUserId();
        ThreadPoolUtil.submit(()->{
            try {
                ContentSearchService contentSearchService = BeanFactory.getInstance().getBean(ContentSearchService.class);
                List<SearchResult> userSearchResult = contentSearchService.searchUsers(search);
                List<SearchResult> groupSearchResult = contentSearchService.searchGroups(userId, search);
                List<SearchResult> messageSearchResult = contentSearchService.searchMessages(userId, search);

                FxThreadUtil.runLater(()->{
                    searchPane.setData(search, userSearchResult, groupSearchResult, messageSearchResult);
                });
            } catch (Exception e) {
                LOGGER.error("查询搜索结果失败", e);
            }
        });
    }

    private void showSession(ImSession imSession, int delayTime) {
        LOGGER.info("select session, id: {}", imSession.getId());
        int selectIdx = sortedList.indexOf(imSession);
        messageSessionList.show(selectIdx);
        if (delayTime < 1) {
            Node selectedNode = messageSessionNodeMap.get(imSession.getId());
            if (selectedNode == null) return;
            selectedNode.fireEvent(new MouseEvent(MouseEvent.MOUSE_CLICKED, 1, 1, 1, 1, MouseButton.PRIMARY, 1,
                    true, true, true, true, true, true, true, true, true, true, null));
        } else {
            ThreadPoolUtil.schedule(()->{
                Node node = messageSessionNodeMap.get(imSession.getId());
                if (node == null) return;
                FxThreadUtil.runLater(()->{
                    node.fireEvent(new MouseEvent(MouseEvent.MOUSE_CLICKED, 1, 1, 1, 1, MouseButton.PRIMARY, 1,
                            true, true, true, true, true, true, true, true, true, true, null));
                });
            }, delayTime);
        }
    }
}
